package com.angel.architecture.exceptions;

import java.lang.reflect.InvocationTargetException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * COMENTARIO AQUI.
 *
 * @author ALeinvand
 * @version $Revision: 1.4.20.8 $
 */
// TODO arquitectura ALeinvand - 29/05/2006: cosas que faltarian:
// 1) manejar mejor el tema del logueo. que identifique por business y non business. o por subclase que lanza
public abstract class ApplicationException extends RuntimeException{


	private static final long serialVersionUID = 5788852910789038230L;
	private static final String EXCEPTION_MESSAGE = "The following stacktrace is related to an error identified as: ";
    private static final Log log= LogFactory.getLog(ApplicationException.class);
    protected String messageString = "noMessage";
    private Collection<?> exceptions = new ArrayList<Object>();

    protected ApplicationException() {
    }

    public ApplicationException(String message) {
        super();
        this.messageString = message;
        // TODO arquitectura ALeinvand - 29/05/2006: pasar el logueo de las exceptions a otro lado
        log.error(EXCEPTION_MESSAGE + " " + this.messageString, this);
    }

    public ApplicationException(Throwable cause) {
        this.messageString = "";
        unwrapException(cause);
        // TODO arquitectura ALeinvand - 29/05/2006: pasar el logueo de las exceptions a otro lado
        log.error(EXCEPTION_MESSAGE + " " + this.messageString,cause);
    }

    public ApplicationException(String message, Throwable cause) {
        super();
        this.messageString = message;
        unwrapException(cause);
        // TODO arquitectura ALeinvand - 29/05/2006: pasar el logueo de las exceptions a otro lado
        log.error(EXCEPTION_MESSAGE + " " + this.messageString,cause);
    }

    public ApplicationException(Collection<?> exceptions) {
        super();
        this.exceptions = exceptions;
        log.error(EXCEPTION_MESSAGE + " " + getMessage());
    }

    /**
     * Este metodo se ocupa de llegar hasta la exception causa real. Si no es null, me quedo con el throwableCause
     * que tenia como mi throwableCause. Si es null, me quedo con el mensaje, y el throwableCause queda en null.
     */
    private void unwrapException(Throwable throwableCause) {
        if (throwableCause instanceof InvocationTargetException ||
                throwableCause instanceof RemoteException ||
                throwableCause instanceof ExceptionInInitializerError ||
                throwableCause instanceof ApplicationException) {
            this.messageString = this.messageString + "\n" + throwableCause.getMessage();
            initCause(throwableCause.getCause());
        } else {
            initCause(throwableCause);
        }
    }

    /**
     * este metodo devuelve el propio mensaje, sin perder si lo hubiera el mensaje de la superclase
     * @return el mensaje armado
     */
    public String getMessage() {
        StringBuffer _stringBuffer = new StringBuffer();
        if (super.getMessage() != null) {
            _stringBuffer.append(super.getMessage() + "\n");
        }
        for (Iterator<?> iter = exceptions.iterator(); iter.hasNext();) {
			Exception e = (Exception) iter.next();
			_stringBuffer.append(e.getMessage()+" ");
		}
        if(!"noMessage".equals(messageString))
        _stringBuffer.append(messageString);
        return _stringBuffer.toString();
    }

    /**
     * METODO SOBRESCRITO
     * @see java.lang.Throwable#initCause(java.lang.Throwable)
     */
    public synchronized Throwable initCause(Throwable cause) {
        Throwable ret = super.initCause(cause);
        if (cause != null) {
            this.messageString = this.messageString + "\n" + cause.getMessage();
        }
        return ret;
    }
}